﻿using System;
using System.Threading.Tasks;
using Devonline.Core;
using Devonline.Entity;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
using static Devonline.Core.Helper;

namespace Devonline.MessageCenter
{
    /// <summary>
    /// 消息集线器
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MessageHub<T> : SignalHub<IMessageClient<T>, Message<T>>
    {
        public MessageHub(ILogger<SignalHub<IMessageClient<T>, Message<T>>> logger, IDistributedCache cache, IHttpContextAccessor httpContextAccessor, McSetting setting) : base(logger, cache, httpContextAccessor, setting)
        {
        }

        /// <summary>
        /// 服务器转发消息的方法
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task Send(Message<T> message)
        {
            try
            {
                //验证消息体
                ValidateMessage(message);

                //处理消息
                await ProcessMessage(message);

                //发送消息到接收者
                await Clients.Group(message.Receiver).Receive(message);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Send message throw exception!");
            }
        }

        /// <summary>
        /// 发送通知消息到指定客户端
        /// 服务器暂不接受通知回执
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task Notice(Message<T> message)
        {
            try
            {
                //验证消息体
                ValidateMessage(message);

                //处理消息
                message.Type = MessageType.Notice;
                await ProcessMessage(message);

                //发送消息到接收者
                await Clients.Group(message.Receiver).Notice(message);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Notice message throw exception!");
            }
        }

        /// <summary>
        /// 发送预警消息到指定客户端
        /// 服务器暂不接受预警回执
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task Alarm(Message<T> message)
        {
            try
            {
                //验证消息体
                ValidateMessage(message);

                //处理消息
                message.Type = MessageType.Alarm;
                await ProcessMessage(message);

                //发送消息到接收者
                await Clients.Group(message.Receiver).Alarm(message);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Alarm message throw exception!");
            }
        }

        /// <summary>
        /// 发送回复消息到指定客户端
        /// 服务器暂不接受预警回执
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task Reply(Message<T> message)
        {
            try
            {
                //验证消息体
                ValidateMessage(message);

                //处理消息
                message.Type = MessageType.Reply;
                await ProcessMessage(message);

                //发送消息到接收者
                await Clients.Group(message.Receiver).Alarm(message);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Reply message throw exception!");
            }
        }

        /// <summary>
        /// 服务器接收心跳消息, 心跳消息为客户端保活信号
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        public async Task Heartbeat(Message message)
        {
            try
            {
                ThrowWhenNull(message, nameof(message));
                message.Id ??= KeyGenerator.GetKey().ToString();
                message.Receiver = AppSettings.USER_SYSTEM;
                message.ReceiveTime = DateTime.UtcNow;
                message.Type = MessageType.Heartbeat;
                message.To = ClientType.Server;
                message.Sender ??= GetUserId();

                _logger.LogInformation("Receive the Heartbeat message from {client} at {time}", message.Sender, DateTime.Now);

                var messageKey = _appSetting.MessageCache + MessageType.Heartbeat.ToString() + AppSettings.CHAR_UNDERLINE + message.Sender;
                await _cache.SetStringAsync(messageKey, message.ToJsonString());
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Receive Heartbeat message throw exception!");
            }
        }

        /// <summary>
        /// 服务器接收消息回执方法
        /// </summary>
        /// <param name="id">消息编号</param>
        /// <returns></returns>
        public async Task Ack(string id)
        {
            try
            {
                if (id.IsNullOrWhiteSpace())
                {
                    throw new Exception("The message id can not be null or whitespace!");
                }

                var messageKey = _appSetting.MessageCache + id;
                var cache = await _cache.GetStringAsync(messageKey);
                if (cache.IsNullOrEmpty())
                {
                    throw new Exception($"The message of id {id} not found!");
                }

                var message = cache.ToJsonObject<Message<T>>();
                message.ReceiveTime = DateTime.UtcNow;
                _logger.LogInformation("Receive the ACK message from {receiver} to {sender} at {time}", message.Receiver, message.Sender, DateTime.Now);

                //保存回执状态
                await _cache.SetStringAsync(messageKey, message.ToJsonString());

                //回执发送消息到发送者
                await Clients.Group(message.Sender).Ack(id);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Receive and send ACK message throw exception!");
            }
        }

        /// <summary>
        /// 服务器接收消息已读方法
        /// </summary>
        /// <param name="id">消息编号</param>
        /// <returns></returns>
        public async Task Read(string id)
        {
            try
            {
                if (id.IsNullOrWhiteSpace())
                {
                    throw new Exception("The message id can not be null or whitespace!");
                }

                var messageKey = _appSetting.MessageCache + id;
                var cache = await _cache.GetStringAsync(messageKey);
                if (cache.IsNullOrEmpty())
                {
                    throw new Exception($"The message of id {id} not found!");
                }

                var message = cache.ToJsonObject<Message<T>>();
                message.IsRead = true;
                message.ReadTime = DateTime.UtcNow;
                _logger.LogInformation("Read message from {receiver} to {sender} at {time}", cache, message.Receiver, message.Sender, DateTime.Now);

                //保存已读的消息
                await _cache.SetStringAsync(messageKey, message.ToJsonString());

                //将消息已读状态发送到发送者
                await Clients.Group(message.Sender).Read(id);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Read and send message throw exception!");
            }
        }

        /// <summary>
        /// 验证消息体
        /// </summary>
        /// <param name="message"></param>
        /// <exception cref="Exception"></exception>
        private void ValidateMessage(Message<T> message)
        {
            ThrowWhenNull(message, nameof(message));
            ThrowWhenNull(message.Content, "The message content");

            if (message.Receiver.IsNullOrEmpty() && message.Group.IsNullOrEmpty())
            {
                throw new Exception("The message has no receiver or receive group!");
            }
        }

        /// <summary>
        /// 通用消息处理
        /// </summary>
        /// <param name="message"></param>
        /// <returns></returns>
        private async Task ProcessMessage(Message<T> message)
        {
            //填充消息编号和消息当前连接
            message.Id ??= KeyGenerator.GetKey().ToString();
            message.Sender ??= GetUserId();
            message.SendTime ??= DateTime.UtcNow;

            //将收到的消息先保存到缓存中
            var content = message.ToJsonString();
            var receiver = message.Group ?? string.Empty;
            receiver += (receiver.IsNullOrWhiteSpace() ? string.Empty : "@") + message.Receiver;

            _logger.LogInformation("Message Center receive the {messageType} message {message} from {sender} to {receiver} at {time}", message.Type, content, message.Sender, receiver, DateTime.Now);
            var messageKey = _appSetting.MessageCache + message.Id;
            await _cache.SetStringAsync(messageKey, content);
        }
    }

    /// <summary>
    /// 文本内容消息集线器
    /// </summary>
    public class TextMessageHub : MessageHub<string>
    {
        public TextMessageHub(ILogger<SignalHub<IMessageClient<string>, Message<string>>> logger, IDistributedCache cache, IHttpContextAccessor httpContextAccessor, McSetting setting) : base(logger, cache, httpContextAccessor, setting)
        {
        }
    }
}
